/*
 * BTree.cpp
 *
 *  Created on: Nov 21, 2011
 *      Author: kimi
 */

#include "BTree.h"

using namespace std;

BTree::BTree(string _filePath,unsigned long xBlockSize) {
	blockSize = xBlockSize;
	filePath = _filePath;

	BTREE_MAX_NODE = 5;

	bTreeIndex = new BTreeIndex<unsigned long,BlockRecordValue>(BTREE_MAX_NODE,filePath + "_Index");
	bTreeIndex->load();

	bTreeData = new BlocksFile(filePath + "_Data",blockSize);
}

BTree::~BTree() {
	bTreeIndex->save();
	delete bTreeIndex;
	delete bTreeData;
}

/**
 *
 */
bool BTree::put(unsigned long key, char* data, unsigned long data_len) {
	bool res = false, added = false;

	if (bTreeIndex->has(key)) {
		res = remove(key);
		if (res == false)
			cerr << "ERROR DELETE"<< endl;
	}
	else {
		res = true;
	}

	unsigned long idBlock = 0;
	unsigned long idRecord = 0;

	if (res == true) {

		unsigned long cantBlocks = bTreeData->getBlocksCount();

		if (cantBlocks == 0) {
			Block* block = NULL;
			if (bTreeData->createNewBlock(&block) == false) {
				cerr << "Error al crear el bloque" << endl;
				return res;
			}
			cantBlocks = bTreeData->getBlocksCount();
		}

		for (idBlock=0;idBlock<cantBlocks && added == false;idBlock++) {
			Block* block = NULL;

			bTreeData->getBlockById(idBlock,&block);

			if(block->getFreeSpace() >= sizeof(unsigned long)+data_len){
				idRecord = block->getRecordCount();

				if (block->addRecord(data,data_len)) {
					BlockRecordValue brv;
					brv.blockId = idBlock;
					brv.recordId = idRecord;

			//		cout << "INSERT: " << idBlock << " - " << idRecord << endl;

					if (bTreeIndex->put(key,brv)) {
						added = true;
					}
					else {
						cerr << "ERROR DELETE 2" << endl;
						block->deleteRecord(idRecord);
					}

					if (bTreeData->updateBlock(block) == false) {
						cerr << "Error al escribir el bloque" << endl;
					}
				}
			}


			if (block != NULL) {
				delete block;
			}
		}

		if (added == false && idBlock >= cantBlocks) {
			//all blocks full...new block
			Block* block = NULL;
			if (bTreeData->createNewBlock(&block)) {
				idRecord = block->getRecordCount();

				if (block->addRecord(data,data_len)) {
					BlockRecordValue brv;
					idBlock = bTreeData->getBlocksCount()-1;
					brv.blockId = idBlock;
					brv.recordId = idRecord;

				//	cout << "INSERT: " << idBlock << " - " << idRecord << endl;

					if (bTreeIndex->put(key,brv)) {
						added = true;
					}
					else {
						cerr << "ERROR DELETE 3" << endl;
						block->deleteRecord(idRecord);
					}

					if (bTreeData->updateBlock(block) == false) {
						cerr << "Error al escribir el bloque" << endl;
					}
				}

				delete block;
			}
		}
	}

	return added;
}

/**
 *
 */
bool BTree::get(unsigned long key, char** data, unsigned long* data_len) {
	bool res = false;

	if (bTreeIndex->has(key)) {
		BlockRecordValue brv = bTreeIndex->get(key);
		unsigned long blockId = brv.blockId;
		unsigned long recId = brv.recordId;

	//	cout << "BRV.bid " << blockId << " - BRV.ric " << recId << endl;

		Block *block = NULL;
		if (bTreeData->getBlockById(blockId,&block) == true) {
			if (block->getRecord(recId,data,data_len) == true) {
				res = true;
			}
		}
		if (block != NULL) {
			delete block;
		}
	}

	return res;
}


/**
 *
 */
bool BTree::has(unsigned long key) {
	bool res = false;

	if (bTreeIndex->has(key)) {
		res = true;
	}

	return res;
}

bool BTree::remove(unsigned long key) {
	bool res = false;

	BlockRecordValue brv = bTreeIndex->get(key);
	unsigned long blockId = brv.blockId;
	unsigned long recId = brv.recordId;

	Block *block = NULL;
	char* data = NULL;
	unsigned long data_len = 0;

	if (bTreeData->getBlockById(blockId,&block) == true) {
		if (block->deleteRecord(recId) == true) {
			if (bTreeData->updateBlock(block) == true) {
				bTreeIndex->remove(key);
				res = true;
			}
			else {
				cerr << "Error al actualizar el registro del bloque." << endl;
			}
		}
		else {
			cerr << "Error al borrar el registro del bloque." << endl;
		}
	}
	else {
		cerr << "Error al obtener el bloque." << endl;
	}


	return res;
}
